package fudan.se.project.service.infrastructure.base;


import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.FileUtils;
import org.springframework.core.io.InputStreamResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Objects;

@Slf4j
@Service
public class StorageService {

    private static final String BASE_PATH = "../uploads/";

    static {
        cleanUploads();
    }

    private static void cleanUploads() {
        File file = new File(BASE_PATH);
        if (file.exists()) {
            try {
                FileUtils.cleanDirectory(file);
                log.info("uploads cleaned up");
            } catch (IOException e) {
                log.warn("uploads clean-up failed");
            }
        }
    }

    public boolean store(MultipartFile multipartFile, String relativePath) {
        boolean result = false;

        String path = StringUtils.applyRelativePath(BASE_PATH, relativePath);

        File file = new File(path);

        if (!file.exists()) {
            File parent = file.getParentFile();
            if (!parent.exists()) {
                parent.mkdirs();
            }

            try {
                result = file.createNewFile();
            } catch (IOException e) {
                log.warn("file creates io exception: " + path);
                return false;
            }
        }

        try {
            multipartFile.transferTo(file.getAbsoluteFile());
        } catch (IOException e) {
            log.warn("multipart file transfers io exception: " + path);
            return false;
        }

        return result;
    }

    @SneakyThrows
    public InputStreamResource downLoad(String relativePath) {
        String path = StringUtils.applyRelativePath(BASE_PATH, relativePath);
        File file = new File(path);
        if (file.exists()) {
            return new StorageInputStreamResource(file.getAbsoluteFile());
        } else return null;
    }

    public boolean delete(String relativePath) {
        boolean result;

        String path = StringUtils.applyRelativePath(BASE_PATH, relativePath);

        File file = new File(path);

        try {
            result = Files.deleteIfExists(file.toPath());
        } catch (IOException e) {
            log.warn("file not exist");
            return false;
        }

        return result;
    }

    private static class StorageInputStreamResource extends InputStreamResource {

        private final File file;

        public StorageInputStreamResource(File file) throws FileNotFoundException {
            super(new FileInputStream(file));
            this.file = file;
        }

        @Override
        public File getFile() throws IOException {
            return file;
        }

        @Override
        public String getFilename() {
            return file.getName();
        }

        @Override
        public boolean isFile() {
            return this.file.isFile();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof StorageInputStreamResource)) return false;
            if (!super.equals(o)) return false;
            StorageInputStreamResource that = (StorageInputStreamResource) o;
            return Objects.equals(isFile(), that.isFile());
        }

        @Override
        public int hashCode() {
            return Objects.hash(super.hashCode(), isFile());
        }
    }

    public static String getBasePath() {
        return BASE_PATH;
    }
}
